﻿#include "study09.h"
#include "test_base.h"

// C/C++ 标准库学习网站：https://legacy.cplusplus.com
// C/C++ 微软(Visual Studio)中文学习网站：https://learn.microsoft.com/zh-cn/cpp/cpp/?view=msvc-170

/*
C++ 的联合体同样不能有函数成员

虚析构函数：
	virtual 关键字除了实现多态，还可以用来实现虚析构函数。
	使用多态创建派生类对象时，在该对象被删除时，默认是调用基类的析构函数；不会自动调用派生类的析构函数；
	多态派生类对象被删除时，如果基类有虚析构函数，则会调用派生类的析构函数，否则只会调用基类的析构函数。
	虚析构函数的声明：virtual ~ClassName() {...} // 注意是在基类的 public 中声明

在 C++ 中主要有四种类型转换的方式：
	static_cast<T>(expression) // 静态类型转换，将 expression 转换为 T 类型
	const_cast<T>(expression) // 常量类型转换，将 expression 转换为 T 类型，但不改变 expression 的 const 属性
	reinterpret_cast<T>(expression) // 重新解释类型转换，将 expression 转换为 T 类型，但不进行类型安全检查，主要荣誉类型双关
	dynamic_cast<T>(expression) // 动态类型转换（主要用于多态），将 expression 转换为 T 类型，不进行类型安全检查
		dynamic_cast 会存储运行时的类型信息（占用部分内存、性能开销），不像使用动态类型转换可以关闭运行时类型信息：
			右键当前项目文件名->属性->C/C++->语言->启用运行时类型信息->否（注意平台环境）；
			这样就可以关闭运行时类型信息，使得 dynamic_cast 失效（使用的时候报错），从而避免运行时类型信息的存储。

在 vs 中，调试模式下可以选择条件和操作：
	1.条件断点：当满足某个条件时，断点会自动触发；比如：当变量 a 等于 10 时，断点会自动触发；之前的语句会全部执行
	2.数据断点：当某个变量的值发生变化时，断点会自动触发；比如：当变量 a >= 20 时，输出变量 a 的值；
	注意：
		1.条件断点和数据断点只能在调试模式下使用；
		2.条件断点和数据断点只能在程序运行到断点处停止；
		3.条件断点和数据断点只能在程序运行到断点处执行；

预编译头文件操作：
	预编译头文件是为了提高编译速度，减少重复编译，提高编译效率。
	查看项目文件预编译后的代码文件（.i 文件）操作：
		右键当前项目文件名->属性->C/C++->预处理器->预处理到文件->是（注意平台环境）；
		然后：右键当前项目文件名->生成；这样就可以在对应环境下到一个与每一个 .cpp 文件同名的预编译头文件（.i 文件）。
	可以将头文件中的预处理代码或者包含的文件进行预编译为二进制文件：
		单独创建一个 .cpp 文件（本项目为：预编译头文件.cpp），
			只包含预处理头文件标头文件，本项目为 "类型关联标头.h"；
		然后：右键当前项目文件名->属性->C/C++->预编译头->预编译头->使用->（注意平台环境）
			预编译头文件->改名为自己创建的预编译头文件名（本项目的 类型关联标头.h 系统默认的名为 stdafx.h）；
		最后：右键需要创建预处理头文件的 cpp 文件（本项目为：预编译头文件.cpp）->
			属性->C/C++->预编译头->预编译头->创建
		设置好上面选项后，再次编译项目（右键当前项目文件名->生成），预编译头文件会自动生成、应用。
	查看生成计时设置：
		工具->选项->项目和解决方案->VC++目录->生成计时->是（注意平台环境）；
			这样就可以看到生成预编译头文件的时间了（右键当前项目文件名->生成的输出窗口中会显示耗时）

基准测试注意针对的是程序的耗时（反应性能）的操作，可以用时间计时器来测试；
	即自定一个时间测试类，在测试函数中进行计时，并输出结果。
*/

typedef struct IntInfo {
	int x, y, z;

	IntInfo(int a, int b, int c)
		: x(a), y(b), z(c) {
	}

	int* start() { return &x; }

	Vector2& toVector2() { return *(Vector2*)&x; } // 类型关联，使得 x = Vector2.a, y = Vector2.b
} iif;

int main()
{
	using namespace std; // 命名空间声明

	// 奇怪的操作，两种类型的内存操作（类型双关）：
	{
		print_start("奇怪的操作测试");
		long long a1 = 10;
		//cout << sizeof a1 << endl; // 8
		double d1 = (double)a1; // 隐式类型转换
		double d2 = *(double*)&a1;
		cout << "a1 = " << a1 << ", d1 = " << d1 << ", d2 = " << d2 << endl;
		cout << "---------------------------------------------------\n";

		double& pd1 = *(double*)&a1;
		pd1 = 20.0;
		cout << "a1 = " << a1 << ", d1 = " << d1 << ", pd1 = " << pd1 << endl;
		cout << "---------------------------------------------------\n";

		iif i1(1, 2, 3);
		int* p1_y = (int*)((char*)&i1 + 4);
		cout << "*p1_y = " << *p1_y << ", *(p1_y + 1) = " << *(p1_y + 1) << endl;
		cout << "---------------------------------------------------\n";

		int* p2 = i1.start();
		*p2 = 4;
		*(p2 + 1) = 5;
		p2[2] = 6;
		cout << "i1.x = " << i1.x << ", i1.y = " << i1.y << ", i1.z = " << i1.z << endl;
		cout << "---------------------------------------------------\n";
		Vector2 v1(7, 8);
		i1.toVector2() = v1;
		cout << "i1.x = " << i1.x << ", i1.y = " << i1.y << ", i1.z = " << i1.z << endl;

		print_end("奇怪的操作测试");
	}

	// C++ 联合体：
	{
		print_start("C++联合体测试");
		union UnionTest { // 很多时候在使用联合体是是匿名的，所以很少写联合体类型名（这里的 UnionTest）
			int		a;
			double	b;
			char	c[14];
			//void add(int x, int y) { return x + y; } // err 不能有函数成员
		} u1; // 声明一个联合体变量 u1

		cout << "sizeof(u1) = " << sizeof(u1) << endl; // 16 注意类型的内存对齐方式
		u1.a = 10;
		u1.b = 20.0; // 修改同一块内存
		cout << "u1.a = " << u1.a << ", u1.b = " << u1.b << endl;
		strcpy_s(u1.c, "Hello, world!");
		cout << "u1.a = " << u1.a << ", u1.b = " << u1.b << ", u1.c = " << u1.c << endl;
		cout << "---------------------------------------------------------\n";

		struct Uni {
			union {
				int		u1;
				double	u2;
				char	u3[20];
			};
			int a;
		};
		Uni u2 = { 10, 20 };
		strcpy_s(u2.u3, "i'm too foolish");
		cout << "u2.u1 = " << u2.u1 << ", u2.u2 = " << u2.u2
			<< ", u2.u3 = " << u2.u3 << ", u2.a = " << u2.a << endl;
		cout << "---------------------------------------------------------\n";
		struct Vector4
		{
			union
			{
				struct {
					int x, y, z, w;
				};
				struct {
					Vector2 va, vb; // 使得 x = va.a, y = va.b, z = vb.a, w = vb.b 类型双关
				};
			};
			void print_v(Vector2& v1) {
				cout << v1.a << ",  " << v1.b << endl;
			}
		};
		cout << "sizeof(Vector4) = " << sizeof(Vector4) << endl; // 16
		Vector4 v4 = { 1, 2, 3, 4 };
		cout << "v4.x = " << v4.x << ", v4.y = " << v4.y <<
			", v4.z = " << v4.z << ", v4.w = " << v4.w << endl;
		v4.va.b = 500;
		v4.vb.b = 600;
		v4.print_v(v4.va);
		v4.print_v(v4.vb);
		/*Vector2 v5 = { 7, 8 };
		v4.vb = v5; */
		cout << "v4.x = " << v4.x << ", v4.y = " << v4.y <<
			", v4.z = " << v4.z << ", v4.w = " << v4.w << endl;

		print_end("C++联合体测试");
	}

	// 虚析构函数与类型转换：
	{
		print_start("虚析构函数与类型转换测试");
		test_virtual_destructor();

		// 类型转换：
		test_type_conversion();

		print_end("虚析构函数与类型转换测试");
	}

	// 基准测试：
	{
		print_start("基准测试");
		// 测试循环 4000000000 次累加耗时
		//test_loop_add();
		{
			Timer timer;
			long long sum = 0;
			for (size_t i = 0; i < 4000000000; i++) {
				sum += 2;
			} // 通过调试->转到反汇编; 可以看到在 Release 模式下并不进行实际的加法循环运算，而是直接返回结果
			std::cout << "sum: " << sum << std::endl;
		}

		// 简单测试智能指针的性能
		test_smart_pointer();
		cout << "调试断点\n";
		__debugbreak(); // 调试断点函数调用，可用于 Release 调试

		print_end("基准测试");
	}

	return 0;
}
